//////////////////////////////////////////////////////////////////////
//
//	Crytek Common Source code
//	
//	File: Cry_Vector3.h
//	Description: Common vector class
//
//	History:
//	-Feb 27,2003: Created by Ivo Herzeg
//
//////////////////////////////////////////////////////////////////////

#ifndef VECTOR4_H
#define VECTOR4_H

#if _MSC_VER > 1000
# pragma once
#endif


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// class Vec4_tpl
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

template <typename F> struct Vec4_tpl
{

	F x,y,z,w;

#if defined(_DEBUG) && !defined(__SPU__)
	ILINE Vec4_tpl() 
	{
		if (sizeof(F)==4)
		{
			uint32* p=alias_cast<uint32*>(&x);		p[0]=F32NAN;	p[1]=F32NAN; p[2]=F32NAN; p[3]=F32NAN;
		}
		if (sizeof(F)==8)
		{
			uint64* p=alias_cast<uint64*>(&x);		p[0]=F64NAN;	p[1]=F64NAN; p[2]=F64NAN; p[3]=F64NAN;
		}

	}
#else
	ILINE Vec4_tpl()	{};
#endif

	template<typename F2>
	ILINE Vec4_tpl<F>& operator = (const Vec4_tpl<F2> &v1) 
	{
		x=F(v1.x); y=F(v1.y); z=F(v1.z); w=F(v1.w);
		return (*this);
	}

	ILINE Vec4_tpl( F vx, F vy, F vz, F vw ) { x=vx; y=vy; z=vz; w=vw; };
	ILINE Vec4_tpl( const Vec3_tpl<F> &v, F vw ) {  x=v.x; y=v.y; z=v.z; w=vw; };

	ILINE void operator () ( F vx, F vy, F vz, F vw ) { x=vx; y=vy; z=vz; w=vw; };
	ILINE void operator () ( const Vec3_tpl<F>& v, F vw ) {  x=v.x; y=v.y; z=v.z; vw=vw; };

	ILINE F &operator [] (int index)		  { assert(index>=0 && index<=3); CHECK_SIMD_ALIGNMENT; return ((F*)this)[index]; }
	ILINE F operator [] (int index) const { assert(index>=0 && index<=3); CHECK_SIMD_ALIGNMENT; return ((F*)this)[index]; }
	template <class T> ILINE  Vec4_tpl( const Vec4_tpl<T>& v ) : x((F)v.x), y((F)v.y), z((F)v.z), w((F)v.w) { assert(this->IsValid()); }

	ILINE bool IsEquivalent(const Vec4_tpl<F>& v1, F epsilon=VEC_EPSILON) const 
	{
		assert(v1.IsValid()); 
		assert(this->IsValid()); 
		return  ((fabs_tpl(x-v1.x) <= epsilon) &&	(fabs_tpl(y-v1.y) <= epsilon)&&	(fabs_tpl(z-v1.z) <= epsilon) && (fabs_tpl(w-v1.w) <= epsilon));	
	}


	ILINE Vec4_tpl& operator=( const Vec4_tpl& src ) 
	{
		x=src.x; y=src.y; z=src.z; w=src.w; return *this; 
	}

	ILINE Vec4_tpl<F> operator * (F k) const 
	{ 
		return Vec4_tpl<F>(x*k,y*k,z*k,w*k); 
	}
	ILINE Vec4_tpl<F> operator / (F k) const 
	{ 
		k=(F)1.0/k; 
		return Vec4_tpl<F>(x*k,y*k,z*k,w*k); 
	}

	ILINE Vec4_tpl<F>& operator *= (F k) 
	{ 
		x*=k;		y*=k;	z*=k;		w*=k; 		return *this; 
	}
	ILINE Vec4_tpl<F>& operator /= (F k) 
	{ 
		k=(F)1.0/k; 
		x*=k;		y*=k;		z*=k;		w*=k; 
		return *this; 
	}

	ILINE F Dot (const Vec4_tpl<F> &vec2)	const	
	{ 
		return x*vec2.x + y*vec2.y + z*vec2.z + w*vec2.w; 
	}
	ILINE F GetLength() const 
	{ 
		return sqrt_tpl(Dot(*this)); 
	}

	bool IsValid() const
	{
		if (!NumberValid(x)) return false;
		if (!NumberValid(y)) return false;
		if (!NumberValid(z)) return false;
		if (!NumberValid(w)) return false;
		return true;
	}

	/*!
	* functions and operators to compare vector   
	* 
	* Example:
	*  if (v0==v1) dosomething;
	*/
	ILINE bool operator==(const Vec4_tpl<F> &vec)
	{
		return x == vec.x && y == vec.y && z == vec.z && w == vec.w;
	}
	ILINE bool operator!=(const Vec4_tpl<F> &vec) { return !(*this == vec); }

	ILINE friend bool operator ==(const Vec4_tpl<F> &v0, const Vec4_tpl<F> &v1)
	{
		return ((v0.x==v1.x) && (v0.y==v1.y) && (v0.z==v1.z) && (v0.w==v1.w));
	}
	ILINE friend bool operator !=(const Vec4_tpl<F> &v0, const Vec4_tpl<F> &v1)	{	return !(v0==v1);	}

	//! normalize the vector
	// The default Normalize function is in fact "safe". 0 vectors remain unchanged.
	ILINE void	Normalize() 
	{
		assert(this->IsValid()); 
		F fInvLen = isqrt_safe_tpl( x*x + y*y + z*z + w*w );
		x*=fInvLen; y*=fInvLen; z*=fInvLen; w*= fInvLen;
	}

	//! may be faster and less accurate
	ILINE void NormalizeFast() 
	{
		assert(this->IsValid()); 
		F fInvLen = isqrt_fast_tpl( x*x + y*y + z*z + w*w );
		x*=fInvLen; y*=fInvLen; z*=fInvLen; w*= fInvLen;
	}

	ILINE void SetLerp( const Vec4_tpl<F> &p, const Vec4_tpl<F> &q, F t ) {	*this = p*(1.0f-t) + q*t;}
	ILINE static Vec4_tpl<F> CreateLerp( const Vec4_tpl<F> &p, const Vec4_tpl<F> &q, F t ) {	return p*(1.0f-t) + q*t;}

	AUTO_STRUCT_INFO
}; 


typedef Vec4_tpl<f32>		Vec4;			//we will use only this throughout the project
typedef Vec4_tpl<real>	Vec4r;		//for systems with float precision higher then 64bit

#if defined(WIN32) || defined(WIN64) || defined(LINUX) || defined(APPLE) || defined(ORBIS)
typedef Vec4_tpl<f32> Vec4A;
#elif defined(DURANGO)
typedef DEFINE_ALIGNED_DATA(Vec4_tpl<f32>, Vec4A, 16); 
#endif

//vector self-addition
template<class F1,class F2>
ILINE Vec4_tpl<F1>& operator += (Vec4_tpl<F1> &v0, const Vec4_tpl<F2> &v1) 
{
	v0=v0+v1;	return v0;
}

//vector addition
template<class F1,class F2>
ILINE Vec4_tpl<F1> operator + (const Vec4_tpl<F1> &v0, const Vec4_tpl<F2> &v1) 
{
	return Vec4_tpl<F1>(v0.x+v1.x, v0.y+v1.y, v0.z+v1.z, v0.w+v1.w);
}
//vector subtraction
template<class F1,class F2>
ILINE Vec4_tpl<F1> operator - (const Vec4_tpl<F1> &v0, const Vec4_tpl<F2> &v1) 
{
	return Vec4_tpl<F1>(v0.x-v1.x, v0.y-v1.y, v0.z-v1.z, v0.w-v1.w);
}

//vector multiplication
template<class F1,class F2>
ILINE Vec4_tpl<F1> operator * (const Vec4_tpl<F1> v0, const Vec4_tpl<F2> &v1) 
{
	return Vec4_tpl<F1>(v0.x*v1.x, v0.y*v1.y, v0.z*v1.z, v0.w*v1.w);
}

//vector division
template<class F1,class F2>
ILINE Vec4_tpl<F1> operator / (const Vec4_tpl<F1> &v0, const Vec4_tpl<F2> &v1) 
{
	return Vec4_tpl<F1>(v0.x/v1.x, v0.y/v1.y, v0.z/v1.z, v0.w/v1.w);
}


#endif //vector
